//
// Created by 崔世强 on 2018/3/9.
// Copyright (c) 2018 Facebook. All rights reserved.
//

import Foundation
import WCDBSwift
import UIKit
import SwiftyJSON
import Kingfisher
import SwiftHash

class ChatService {
    
    func sendFileMsg(_ url:URL,to:Session,progress: @escaping (_ progress:Double) -> (),onSuccess:@escaping (_ file:URL) -> (),onError: @escaping (_ err:Swift.Error?)-> ()) {
        do {
            let db = SQLiteHelper.getDb()
            try db.run(transaction: { _ in
                
                let file = FileManager.default.displayName(atPath: url.path)
                let array = file.components(separatedBy: ".")
                let fileName = array[0]
                let ext = array.count == 2 ? array[1] : ""
                let size = try FileManager.default.attributesOfItem(atPath: url.path)[.size] as! Int
                let remote = NSUUID().uuidString.replacingOccurrences(of:"-",with: "") + ".\(ext)"
                let obj:[String:Any] = ["name":fileName,"ext":ext,"size":size/1024 ,"remote":remote]
                let json = JSON(obj)
                let msgContent = json.rawString()
                
                let msg = Message()
                msg.sessionServerId = to.sessionServerId
                msg.content = msgContent
                msg.msgType = MessageType.file.rawValue
                msg.chatType = to.chatType
                msg.me = true
                msg.unread = false
                msg.userId = AppStatusManager.instance.userLoginStatus.1 ?? 0
                try MessageDao.insert(msg)
                
                
                var message = Dictionary<String, Any>()
                message["id"] = msg.lastInsertedRowID
                message["localId"] = msg.lastInsertedRowID
                message["syncKey"] = 0
                message["sessionId"] = to.id
                message["sessionServerId"] = to.sessionServerId
                message["chatType"] = to.chatType.rawValue
                message["userId"] = msg.userId
                message["messageType"] = msg.messageType.rawValue
                message["messageContent"] = msgContent
                message["me"] = Int(1)
                message["userName"] = AppStatusManager.instance.userLoginStatus.2
                message["fromName"] = AppStatusManager.instance.userLoginStatus.2
                message["ts"] = msg.ts.timeIntervalSince1970
                message["avatar"] = AppStatusManager.instance.avatar
                message["at"] = []
                
                DispatchQueue.global().async {
                    OSSTool.instance.putFile(url, name: remote,progress:progress,onSuccess: { file in
                        onSuccess(file)
                        do {
                            let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0] as URL
                            let fileUrl = cacheDir.appendingPathComponent("files/\(remote)", isDirectory: false)
                            try FileManager.default.moveItem(atPath: url.path, toPath: fileUrl.path )
                        } catch {
                            print(error)
                        }
                    },onError: onError)
                    ConnectionManager.instance.sendMessage(message: message)
                }
                let notificationNam = Notification.Name("onNewMsg")
                NotificationCenter.default.post(name: notificationNam, object: nil, userInfo: ["list": [message]])
            })
            
        }catch {
            print(error)
        }
    }
    
    
    func sendFileMsg(_ url:URL,to:Group,progress: @escaping (_ progress:Double) -> (),onSuccess:@escaping (_ file:URL) -> (),onError: @escaping (_ err:Swift.Error?)-> ()) {
        do {
            let db = SQLiteHelper.getDb()
            try db.run(transaction: { _ in
                
                let file = FileManager.default.displayName(atPath: url.path)
                let array = file.components(separatedBy: ".")
                let fileName = array[0]
                let ext = array.count == 2 ? array[1] : ""
                let size = try FileManager.default.attributesOfItem(atPath: url.path)[.size] as! Int
                let remote = NSUUID().uuidString.replacingOccurrences(of:"-",with: "") + ".\(ext)"
                let obj:[String:Any] = ["name":fileName,"ext":ext,"size":size/1024 ,"remote":remote]
                let json = JSON(obj)
                let msgContent = json.rawString()
                
                let msg = Message()
                msg.sessionServerId = to.groupId
                msg.content = msgContent
                msg.msgType = MessageType.file.rawValue
                msg.chatType = .group
                msg.me = true
                msg.unread = false
                msg.userId = AppStatusManager.instance.userLoginStatus.1 ?? 0
                try MessageDao.insert(msg)
                
                
                var message = Dictionary<String, Any>()
                message["id"] = msg.lastInsertedRowID
                message["localId"] = msg.lastInsertedRowID
                message["syncKey"] = 0
                message["sessionId"] = 0
                message["sessionServerId"] = to.groupId
                message["chatType"] = ChatType.group.rawValue
                message["userId"] = msg.userId
                message["messageType"] = msg.messageType.rawValue
                message["messageContent"] = msgContent
                message["me"] = Int(1)
                message["userName"] = AppStatusManager.instance.userLoginStatus.2
                message["fromName"] = AppStatusManager.instance.userLoginStatus.2
                message["ts"] = msg.ts.timeIntervalSince1970
                message["avatar"] = AppStatusManager.instance.avatar
                message["at"] = []
                
                DispatchQueue.global().async {
                    OSSTool.instance.putFile(url, name: remote,progress:progress,onSuccess: { file in
                        onSuccess(file)
                        do {
                            let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0] as URL
                            let fileUrl = cacheDir.appendingPathComponent("files/\(remote)", isDirectory: false)
                            try FileManager.default.moveItem(atPath: url.path, toPath: fileUrl.path )
                        } catch {
                            print(error)
                        }
                    },onError: onError)
                    ConnectionManager.instance.sendMessage(message: message)
                }
                let notificationNam = Notification.Name("onNewMsg")
                NotificationCenter.default.post(name: notificationNam, object: nil, userInfo: ["list": [message]])
            })
            
        }catch {
            print(error)
        }
        
        //        var msg = ChatMessage(sessionId: to, from: AppStatusManager.instance.userLoginStatus.1!, fromName: AppStatusManager.instance.userLoginStatus.2!, messageType: .img)
    }
    
    func sendFileMsg(_ url:URL,to:Contact,progress: @escaping (_ progress:Double) -> (),onSuccess:@escaping (_ file:URL) -> (),onError: @escaping (_ err:Swift.Error?)-> ()) {
        do {
            let db = SQLiteHelper.getDb()
            try db.run(transaction: { _ in
                
                let file = FileManager.default.displayName(atPath: url.path)
                let array = file.components(separatedBy: ".")
                let fileName = array[0]
                let ext = array.count == 2 ? array[1] : ""
                let size = try FileManager.default.attributesOfItem(atPath: url.path)[.size] as! Int
                let remote = NSUUID().uuidString.replacingOccurrences(of:"-",with: "") + ".\(ext)"
                let obj:[String:Any] = ["name":fileName,"ext":ext,"size":size/1024 ,"remote":remote]
                let json = JSON(obj)
                let msgContent = json.rawString()
                
                let msg = Message()
                msg.sessionServerId = to.userId
                msg.content = msgContent
                msg.msgType = MessageType.file.rawValue
                msg.chatType = .p2p
                msg.me = true
                msg.unread = false
                msg.userId = AppStatusManager.instance.userLoginStatus.1 ?? 0
                try MessageDao.insert(msg)
                
                
                var message = Dictionary<String, Any>()
                message["id"] = msg.lastInsertedRowID
                message["localId"] = msg.lastInsertedRowID
                message["syncKey"] = 0
                message["sessionId"] = 0
                message["sessionServerId"] = to.userId
                message["chatType"] = ChatType.p2p.rawValue
                message["userId"] = msg.userId
                message["messageType"] = msg.messageType.rawValue
                message["messageContent"] = msgContent
                message["me"] = Int(1)
                message["userName"] = AppStatusManager.instance.userLoginStatus.2
                message["fromName"] = AppStatusManager.instance.userLoginStatus.2
                message["ts"] = msg.ts.timeIntervalSince1970
                message["avatar"] = AppStatusManager.instance.avatar
                message["at"] = []
                
                DispatchQueue.global().async {
                    OSSTool.instance.putFile(url, name: remote,progress:progress,onSuccess: { file in
                        onSuccess(file)
                        do {
                            let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0] as URL
                            let fileUrl = cacheDir.appendingPathComponent("files/\(remote)", isDirectory: false)
                            try FileManager.default.moveItem(atPath: url.path, toPath: fileUrl.path ?? "")
                        } catch {
                            print(error)
                        }
                    },onError: onError)
                    ConnectionManager.instance.sendMessage(message: message)
                }
                var notificationNam = Notification.Name("onNewMsg")
                NotificationCenter.default.post(name: notificationNam, object: nil, userInfo: ["list": [message]])
            })
            
        }catch {
            print(error)
        }
        
        //        var msg = ChatMessage(sessionId: to, from: AppStatusManager.instance.userLoginStatus.1!, fromName: AppStatusManager.instance.userLoginStatus.2!, messageType: .img)
    }
    
    
    
    func sendImgMsg(_ img:UIImage,to:Int) -> ChatMessage? {
        let size = img.size  //原始图片尺寸
        let width = size.width
        let height = size.height
        
        if let imageData = img.compressImage(minSize: 60, maxSize: 120, maxLength: 10*1000),let origImg = img.jpegData(compressionQuality: 1) {
            let memSize = origImg.count/1000  //原始图片大小
            print("压缩前图片大小：\(memSize), 压缩后图片大小:\(imageData.count/1000)")
            
            let fileName = NSUUID().uuidString.replacingOccurrences(of:"-",with: "") + ".jpg"
            //            let fileName = origImg.description + ".jpg"
            let str = imageData.base64EncodedString()
            let obj:[String:Any] = ["width":width,"height":height,"size":memSize,"data":str,"fileName":fileName]
            let json = JSON(obj)
            let imgMsgText = json.rawString()
            OSSTool.instance.putImgFile(fileName, imgData: origImg)
            ImageCache.default.store(img, forKey: fileName)
            
            let msg = ChatMessage(sessionId: to, from: AppStatusManager.instance.userLoginStatus.1!, fromName: AppStatusManager.instance.userLoginStatus.2!, messageType: .img)
            msg.imgData = origImg
            msg.imgSize = size
            msg.text = imgMsgText
            msg.avatar = AppStatusManager.instance.avatar
            msg.imgFileName = fileName
            msg.memSize = memSize
            msg.download = true
            return msg
        }
        return nil
    }
    
    func compressImg(_ img:UIImage,to:Int) -> ChatMessage? {
        let size = img.size  //原始图片尺寸
        let width = size.width
        let height = size.height
        
        if let imageData = img.compressImage(minSize: 60, maxSize: 120, maxLength: 10*1000),let origImg = img.jpegData(compressionQuality: 0.1) {
            let memSize = origImg.count/1000  //原始图片大小
            print("压缩前图片大小：\(memSize), 压缩后图片大小:\(imageData.count/1000)")
            
            let fileName = NSUUID().uuidString.replacingOccurrences(of:"-",with: "") + ".jpg"
            //            let fileName = origImg.description + ".jpg"
            let str = imageData.base64EncodedString()
            let obj:[String:Any] = ["width":width,"height":height,"size":memSize,"data":str,"fileName":fileName]
            let json = JSON(obj)
            let imgMsgText = json.rawString()
            ImageCache.default.store(img, forKey: fileName)
            let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0] as URL
            let fileUrl = cacheDir.appendingPathComponent("\(fileName)", isDirectory: false)
            try? origImg.write(to: fileUrl)
            let msg = ChatMessage(sessionId: to, from: AppStatusManager.instance.userLoginStatus.1!, fromName: AppStatusManager.instance.userLoginStatus.2!, messageType: .img)
            msg.imgData = origImg
            msg.imgSize = size
            msg.text = imgMsgText
            msg.avatar = AppStatusManager.instance.avatar
            msg.imgFileName = fileName
            msg.memSize = memSize
            msg.download = true
            return msg
        }
        return nil
    }
    
    func sendMsgDirect(msg:ChatMessage,result: @escaping (_ message:ChatMessage) -> Void) {
        do {
            let msgToSave = msg.toMessage()
            msgToSave.unread = false
            msgToSave.status = .pending
            try MessageDao.insert(msgToSave)
            msgToSave.id = Int(msgToSave.lastInsertedRowID)
            msg.localId = Int(msgToSave.lastInsertedRowID)
            
            ConnectionManager.instance.sendMessage(message: msg, result: { msg in
                do {
                    msgToSave.status = msg.status
                    try MessageDao.updateMsgState(msg: msgToSave)
                }catch {
                    print(error)
                }
                result(msg)
            })
        }catch {
            msg.status = .error
            result(msg)
        }
    }
    
    
    func sendSingleOrGroupMsg(msg:ChatMessage,result: @escaping (_ message:ChatMessage) -> Void) {
        do {
            let msgToSave = msg.toMessage()
            msgToSave.unread = false
            msgToSave.status = .pending
            try MessageDao.insert(msgToSave)
            msgToSave.id = Int(msgToSave.lastInsertedRowID)
            msg.localId = Int(msgToSave.lastInsertedRowID)
            
            switch msg.messageType {
            case .img:
                let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0] as URL
                let fileUrl = cacheDir.appendingPathComponent("\(msg.imgFileName ?? "")", isDirectory: false)
                IM.request(.upload(dir: "imgs/", file: fileUrl.path, filename: msg.imgFileName ?? ""), success: { json in
                    if reqSuccess(json["code"].stringValue) {
                        ConnectionManager.instance.sendMessage(message: msg, result: { msg in
                            do {
                                msgToSave.status = msg.status
                                try MessageDao.updateMsgState(msg: msgToSave)
                            }catch {
                                print(error)
                            }
                            
                            result(msg)
                        })
                    } else {
                        msg.status = .error
                        do {
                            msgToSave.status = msg.status
                            try MessageDao.updateMsgState(msg: msgToSave)
                        }catch {
                            print(error)
                        }
                        result(msg)
                    }
                }, error: { code in
                    print(code)
                    msg.status = .error
                    do {
                        msgToSave.status = msg.status
                        try MessageDao.updateMsgState(msg: msgToSave)
                    }catch {
                        print(error)
                    }
                    result(msg)
                }, failure: { error in
                    msg.status = .error
                    do {
                        msgToSave.status = msg.status
                        try MessageDao.updateMsgState(msg: msgToSave)
                    }catch {
                        print(error)
                    }
                    result(msg)
                }, progress: {progress in
                    
                })
            case .audio:
                let wavFilePath = AppStatusManager.instance.urlForUser!.appendingPathComponent("audio", isDirectory: true)
                    .appendingPathComponent("\(msg.audioPath ?? "").wav", isDirectory: false)
                let amrFilePath = AppStatusManager.instance.urlForUser!.appendingPathComponent("audio", isDirectory: true)
                    .appendingPathComponent("\(msg.audioPath ?? "").amr", isDirectory: false)
                print(wavFilePath.path)
                if !FileManager.default.fileExists(atPath: amrFilePath.path){
                    AudioRecordUtils.instance.convertAudioFile(filePath: wavFilePath, to: amrFilePath)
                }
                IM.request(.upload(dir: "audios/", file: amrFilePath.path, filename: msg.audioPath ?? "" + ".amr"), success: { json in
                    if reqSuccess(json["code"].stringValue) {
                        ConnectionManager.instance.sendMessage(message: msg, result: { msg in
                            do {
                                msgToSave.status = msg.status
                                try MessageDao.updateMsgState(msg: msgToSave)
                            }catch {
                                print(error)
                            }
                            
                            result(msg)
                        })
                    } else {
                        msg.status = .error
                        do {
                            msgToSave.status = msg.status
                            try MessageDao.updateMsgState(msg: msgToSave)
                        }catch {
                            print(error)
                        }
                        result(msg)
                    }
                }, error: { code in
                    print(code)
                    msg.status = .error
                    do {
                        msgToSave.status = msg.status
                        try MessageDao.updateMsgState(msg: msgToSave)
                    }catch {
                        print(error)
                    }
                    result(msg)
                }, failure: { error in
                    msg.status = .error
                    do {
                        msgToSave.status = msg.status
                        try MessageDao.updateMsgState(msg: msgToSave)
                    }catch {
                        print(error)
                    }
                    result(msg)
                }, progress: {progress in
                    
                })
            case .text:
                ConnectionManager.instance.sendMessage(message: msg, result: { msg in
                    do {
                        msgToSave.status = msg.status
                        try MessageDao.updateMsgState(msg: msgToSave)
                    }catch {
                        print(error)
                    }
                    result(msg)
                })
            case .file:
                let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0] as URL
                let fileUrl = cacheDir.appendingPathComponent("files/\(msg.storeFile!)", isDirectory: false)
                IM.request(.upload(dir: "files/", file: fileUrl.path, filename: msg.storeFile ?? ""), success: { json in
                    if reqSuccess(json["code"].stringValue) {
                        ConnectionManager.instance.sendMessage(message: msg, result: { msg in
                            do {
                                msgToSave.status = msg.status
                                try MessageDao.updateMsgState(msg: msgToSave)
                            }catch {
                                print(error)
                            }
                            
                            result(msg)
                        })
                    } else {
                        msg.status = .error
                        do {
                            msgToSave.status = msg.status
                            try MessageDao.updateMsgState(msg: msgToSave)
                        }catch {
                            print(error)
                        }
                        result(msg)
                    }
                }, error: { code in
                    print(code)
                    msg.status = .error
                    do {
                        msgToSave.status = msg.status
                        try MessageDao.updateMsgState(msg: msgToSave)
                    }catch {
                        print(error)
                    }
                    result(msg)
                }, failure: { error in
                    msg.status = .error
                    do {
                        msgToSave.status = msg.status
                        try MessageDao.updateMsgState(msg: msgToSave)
                    }catch {
                        print(error)
                    }
                    result(msg)
                }, progress: {progress in
                    
                })
            case .video:
                IM.request(.upload(dir: "videos/", file: msg.videoPath!, filename: msg.fileName ?? ""), success: { json in
                    if reqSuccess(json["code"].stringValue) {
                        ConnectionManager.instance.sendMessage(message: msg, result: { msg in
                            do {
                                msgToSave.status = msg.status
                                try MessageDao.updateMsgState(msg: msgToSave)
                            }catch {
                                print(error)
                            }
                            
                            result(msg)
                        })
                    } else {
                        msg.status = .error
                        do {
                            msgToSave.status = msg.status
                            try MessageDao.updateMsgState(msg: msgToSave)
                        }catch {
                            print(error)
                        }
                        result(msg)
                    }
                }, error: { code in
                    print(code)
                    msg.status = .error
                    do {
                        msgToSave.status = msg.status
                        try MessageDao.updateMsgState(msg: msgToSave)
                    }catch {
                        print(error)
                    }
                    result(msg)
                }, failure: { error in
                    msg.status = .error
                    do {
                        msgToSave.status = msg.status
                        try MessageDao.updateMsgState(msg: msgToSave)
                    }catch {
                        print(error)
                    }
                    result(msg)
                }, progress: {progress in
                    
                })
            default:
                ConnectionManager.instance.sendMessage(message: msg, result: { msg in
                    do {
                        msgToSave.status = msg.status
                        try MessageDao.updateMsgState(msg: msgToSave)
                    }catch {
                        print(error)
                    }
                    result(msg)
                })
            }
        }catch {
            msg.status = .error
            result(msg)
        }
    }
    
    func searchImg(fromSession:Int?) -> [ChatMessage] {
        var chatMsgs:[ChatMessage] = []
        if fromSession == nil {
            return chatMsgs
        }
        do {
            let db = Database(at: AppStatusManager.instance.urlForUserDB!)
            try db.run(transaction: { _ in
                let msgs:[Message] = try db.getObjects(on: Message.CodingKeys.all,
                                                       fromTable: Message.table,
                                                       where: Message.CodingKeys.msgType == MessageType.img.rawValue && Message.CodingKeys.sessionServerId == fromSession!,
                                                       orderBy: [Message.CodingKeys.createTime.asExpression().asOrder()])
                for msg in msgs {
                    let json = JSON.init(parseJSON: msg.content ?? "")
                    let friend:Contact? = try db.getObject(on: Contact.CodingKeys.all, fromTable: Contact.table, where: Contact.CodingKeys.userId == msg.userId)
                    let sessionMemberRow:GroupMember? = try db.getObject(on: GroupMember.CodingKeys.all, fromTable: GroupMember.table,
                                                                         where: GroupMember.CodingKeys.groupId == msg.sessionServerId && GroupMember.CodingKeys.userId == msg.userId)
                    let userName = friend?.remarkName ?? sessionMemberRow?.nickName ?? friend?.name ?? "好友"
                    
                    let chatMsg = ChatMessage(sessionId: msg.sessionServerId, from: msg.userId, fromName: userName, messageType: .img)
                    chatMsg.imgFileName = json["fileName"].stringValue
                    chatMsg.imgData = Data(base64Encoded: json["data"].stringValue,options: Data.Base64DecodingOptions(rawValue: 0))
                    let width = json["width"].doubleValue
                    let height = json["height"].doubleValue
                    chatMsg.memSize = json["size"].intValue
                    chatMsg.imgSize = CGSize(width: width, height: height)
                    chatMsgs.append(chatMsg)
                }
            })
            
        }catch {
            print(error)
            //            return []
        }
        return chatMsgs
    }
    
    func sendNoticeMsg(_ msgContent:String,to:Int,chatType: ChatType) {
        sendNoticeMsg(msgContent,to:to, chatType:chatType, sendToServer:true)
    }
    
    func sendNoticeMsg(_ msgContent:String,to:Int,chatType: ChatType, sendToServer:Bool) {
        do {
            guard let session = try SessionDao.getSession(to, chatType: chatType) else {
                return
            }
            
            let msg = Message()
            msg.sessionServerId = to
            msg.content = msgContent
            msg.msgType = MessageType.notice.rawValue
            msg.chatType = session.chatType
            msg.me = true
            msg.unread = false
            msg.userId = AppStatusManager.instance.userLoginStatus.1 ?? 0
            try MessageDao.insert(msg)
            
            
            var message = Dictionary<String, Any>()
            message["id"] = msg.lastInsertedRowID
            message["localId"] = msg.lastInsertedRowID
            message["syncKey"] = 0
            message["sessionId"] = session.id
            message["sessionServerId"] = to
            message["chatType"] = session.chatType.rawValue
            message["userId"] = msg.userId
            message["messageType"] = msg.messageType.rawValue
            message["messageContent"] = msgContent
            message["me"] = Int(1)
            message["userName"] = AppStatusManager.instance.userLoginStatus.2
            message["fromName"] = AppStatusManager.instance.userLoginStatus.2
            message["ts"] = msg.ts.timeIntervalSince1970
            message["avatar"] = AppStatusManager.instance.avatar
            message["at"] = []
            
            if sendToServer {
                ConnectionManager.instance.sendMessage(message: message)
            }
        }catch {
            
        }
        
//        let msg = ChatMessage(sessionId: to, from: AppStatusManager.instance.userLoginStatus.1!, fromName: AppStatusManager.instance.userLoginStatus.2!, messageType: .img)
    }
}
